#!/usr/bin/env bash

# This script takes 2 positional args:
#
#   1: The version of packagespec to upgrade a branch to (e.g. 1.2.3)
#   2: The target branch to upgrade.
#
# It works in a temp directory, so does not interfere with your work tree
# or git index or config.
#
# It does this:
#
#   1. Inspects your remote config for URL remotes.
#   2. Clone this directory into a temp dir.
#   3. Sets the remotes in the clone to match the remotes you have configured.
#   4. Fetches everything from all remotes.
#   5. Determines which remote your target branch is on. If it's more than one
#      remote, then exits with an error as there would be no way to choose.
#   6. Checks out a new branch named packagespec<version>/<target branch name>
#   7. Runs packagespec upgrade -version <version>
#   8. Commits the relevant paths with the message 'packagespec upgrade -version <version>'
#   9. Pushes the new branch to the same remote as the original branch.
#   0. Tells you to open a PR.

# VERSION is the packagespec version to upgrade to.
VERSION="$1"
BRANCH="$2"
FLAG="$3"
REPO_NAME="$4"
BINNAME="$0"
usage() { echo "usage: $BINNAME <packagespec version> <branch name> [-pr PRODUCT_NAME]"; }

if [ -z "$VERSION" ]; then
	usage; exit 1
fi
if [ -z "$BRANCH" ]; then
	usage; exit 1
fi
PR=false
if [ -n "$FLAG" ]; then
	if [ "$FLAG" = "-pr" ]; then
		if [ -z "$REPO_NAME" ]; then
			usage; exit 1
		fi
		PR=true
	else
		usage; exit 1
	fi
fi

set -euo pipefail

declare -A REMOTES

# url_remotes lists remotes along with their push URLs, where they are
# not filesystem paths (i.e. do not start with / or . ).
url_remotes() { git remote -v | grep -F "(push)" | sed -E 's/[[:space:]]+\(push\)//g' | grep -Ev "\t(/|\.)"; }
for R in $(url_remotes | cut -f1); do
	REMOTES[$R]="$(url_remotes | grep -E "^$R\t" | cut -f2)"
done

for R in "${!REMOTES[@]}"; do
	echo "Remote: $R = ${REMOTES[$R]}"
done

TEMP=".upgrade-packagespec"
mkdir -p "$TEMP"
echo "*" > "$TEMP/.gitignore"
CLONEDIR="$TEMP/product-repo"
if ! [ -d "$CLONEDIR/.git" ]; then
	git clone . "$CLONEDIR"
fi
cd "$CLONEDIR"
echo "==> WORKING IN TEMP DIR: $CLONEDIR"
git reset --hard

# Remove existing remotes
for R in $(git remote); do git remote rm "$R"; done

# Add remotes from original checkout dir
for R in "${!REMOTES[@]}"; do
	git remote add "$R" "${REMOTES[$R]}"
done

# Fetch everything from these remotes, ignore errors.
git remote update || true

BRANCH_ESCAPED="$(sed -E -e 's/\./\\./g' -e 's/\+/\\+/g' -e 's/\//\\\//g' <<< "$BRANCH")"

# Determine which remotes the branch is on.
declare -A BRANCH_REMOTES
for R in "${!REMOTES[@]}"; do
	if git branch -a | grep -E "^[[:space:]]*remotes/$R/$BRANCH_ESCAPED\$"; then
		TARGET_REMOTE="$R"
		BRANCH_REMOTES[$R]=1
	fi
done

COUNT="${#BRANCH_REMOTES[@]}"
if [ "$COUNT" -ne "1" ]; then
	echo "==> ERROR: Branch $BRANCH found on $COUNT remotes; want exactly 1"
	exit 1
fi

# Checkout the target update branch.
git checkout "$BRANCH"
git reset --hard "$TARGET_REMOTE/$BRANCH"

NEW_BRANCH="packagespec$VERSION/$BRANCH"
git checkout -B "$NEW_BRANCH" "$BRANCH"

COMMAND="packagespec upgrade -version $VERSION"
echo "==> Running $COMMAND"
$COMMAND

git add .circleci/ packages*.lock packagespec*
git commit -m "$COMMAND"

git push -u "$TARGET_REMOTE" "$NEW_BRANCH"

echo "==> All done: upgrade pushed to branch $NEW_BRANCH on ${REMOTES[$TARGET_REMOTE]}"

echo "==> ACTIONS FOR YOU: Open a PR with base: $BRANCH compare: $NEW_BRANCH"

if ! $PR; then
	exit 0
fi

# Open browser with PR ready:
open https://github.com/hashicorp/$REPO_NAME/compare/$BRANCH...$NEW_BRANCH?expand=1
